home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / misc / TownMaze.lha / TownMaze / townpgmr.doc < prev    next >
Text File  |  1991-08-05  |  40KB  |  840 lines

  1. File townpgmr.doc, the programmers' documentation for townmaze Release 1.1.
  2.  
  3.  
  4. This file documents the C code; see file README for compile and
  5. execute instructions, copyright restrictions, etc.
  6.  
  7. Since townmaze is meant to be a set of software for use by programmers,
  8. there is going to be a _lot_ of programmer documentation.  Each of the
  9. following source code files will be separately described.
  10.  
  11.  townmaze.h       townproto.h
  12.  
  13.  cleanupmap.c     closedoors.c       closegates.c       connectst.c
  14.  filllist.c       fillmaze.c         finishalleys.c     freespace.c
  15.  getargs.c        interiorcell.c     makecourts.c       makegates.c
  16.  makestreet.c     makespace.c        makeunused.c       movefromto.c
  17.  nhbrexists.c     nhbris.c           showdbmaze.c       showlistdet.c
  18.  showlistsum.c    showmaze.c         townmain.c         usage.c
  19.  
  20. But first, an overview.
  21.  
  22. Townmaze works conceptually on a structure of maze cells in a
  23. four-connected (each cell has four neighbors) grid with one of five
  24. statuses:
  25.  
  26. ISOLATED - no neighbor of this cell is a street.
  27.  
  28. LIVE - some neighbor of this cell is a street, and some neighbor of
  29. this cell is an isolated cell. 
  30.  
  31. DEAD - some neighbor of this cell is a street, but no neighbor of this
  32. cell is an isolated cell. 
  33.  
  34. STREET - the passageways for the adventurers; this cell is connected
  35. by a series of other street cells to some origin point for a street. 
  36.  
  37. UNUSED - this cell is solid rock, it may neither become a room nor a
  38. street, nor have a street cell as a neighbor. 
  39.  
  40. The goal is to build a town maze like the upper level of Bard's Tale I,
  41. with a street to every door, and a fairly "double row of brownstones" 
  42. appearance, by looking two neighbor links away, to preserve where
  43. possible room cells in back to back rows.
  44.  
  45. To accomplish this, LIVE cells are used to indicate when at least one
  46. direction from the live cell, the next street is separated by an
  47. ISOLATED cell and some other non-STREET cell on the far side.  This
  48. works fairly well when the streets run very parallel, not so well when
  49. they don't.  Better strategies may well exist, but this one does very
  50. nice mazes for some parameter values. 
  51.  
  52. The basic plan of townmaze is to
  53.  
  54. 1) Accept a set of command line parameters.
  55.  
  56. 2) Allocate two major data structures of the appropriate size in
  57. accordance with those parameters, one a two dimensional array of
  58. characters which will be used to build and display the completed maze,
  59. the other a one dimensional array of structures which is used as a
  60. status list to store the status of each maze cell, and to link maze
  61. cells together in sublists for speed of processing. 
  62.  
  63. 3) Fill in the maze as a solid array of rooms except the corners,
  64. which are blocked off (they're too hard to use), each room without
  65. doors, and fill in the status list and link it together as a list of
  66. ISOLATED cells, except again for the corners, which become UNUSED
  67. cells. 
  68.  
  69. 4) Seed the maze with uniquely numbered street origins at border or
  70. interior cells, and obstacles at interior cells only.  Assure that
  71. different seed cells are far enough apart to allow successful maze
  72. creation.  Update display to match; rooms beside streets get doors
  73. facing the streets; the outside wall next to a street becomes a
  74. "gate".
  75.  
  76. 4a) Seed any UNUSED cells in the interior of the maze.
  77.  
  78. 4b) Seed any "gate" STREET cells along the border of the maze.
  79.  
  80. 4c) Seed any "courtyard" STREET cells in the interior of the maze.
  81.  
  82. 5) Connect the streets by extending them until all streets have the
  83. same street number; merge street numbers when two streets meet.  Again
  84. update the display array to match; rooms are converted to streets by
  85. removing all doors.  Rooms newly adjacent to streets gain a new door
  86. on that street. 
  87.  
  88. 6) When all streets are connected, continue to extend them as "alleys"
  89. until no isolated room cells remain. 
  90.  
  91. 7) Close most of the doors, leaving one open door per room, by
  92. replacing doors by walls.
  93.  
  94. 8) Possibly close some or all gates by replacing them with walls.
  95.  
  96. 9) Clean up "pillars"; bits of wall that have been isolated by being
  97. surrounded by streets.
  98.  
  99. 10) Display the maze.
  100.  
  101. 11) Free the allocated space.
  102.  
  103. 12) Exit.
  104.  
  105. Detailed source module descriptions.
  106.  
  107. -----------------------------------------------------------------------
  108. | townmaze.h 
  109. -----------------------------------------------------------------------
  110.  
  111. This header file is where all the #ifdef controlling #defines are done
  112. (except MAINLINE, in townmain.c, and RAND48, in the make file), the
  113. structures are defined, the defined constants are declared, and the
  114. external variables are declared or defined under #ifdef control. 
  115.  
  116. In particular, the cmaze two dimensional character array for maze
  117. display, the statlist one dimensional structure array for cell
  118. statuses, and the various cell type link list head pointers isolated,
  119. live, dead, street, and unused, and their corresponding cell counters
  120. isolatedct, livect, deadct, streetct, and unusedct, are all defined
  121. here. As well, streetnumct, the count of how many different street
  122. numbers exist, is defined here.
  123.  
  124. As well, the global parameters mazeheight, mazewidth, mazegates,
  125. leftgates, mazecourts, mazeunused, with their appropriate defaults
  126. when not read in from the command line, and randomseed without a
  127. default value, and the derived global parameter listsize, are declared
  128. and defined here.
  129.  
  130. In addition, the integer constants ISOLATED, LIVE, DEAD, STREET, and
  131. UNUSED are defined, and the character constants WALL, VDOOR, HDOOR,
  132. and BLANK.  Also defined here is NOPOINTER, which acts as a null
  133. pointer in the statlist double linked lists, which use indices rather
  134. than pointers for links.
  135.  
  136.  
  137. -----------------------------------------------------------------------
  138. | townproto.h
  139. -----------------------------------------------------------------------
  140.  
  141. This header file contains all the function prototypes, since each
  142. function is in its own separate source module, done in both ANSI C and
  143. old K&R form, conditioned on __STDC__, the compiler defined constant
  144. to identify ANSI C compilers.  The same conditioning is done in each
  145. *.c module for compatibility.
  146.  
  147.  
  148. -----------------------------------------------------------------------
  149. | cleanupmap.c -- cleanmap()
  150. -----------------------------------------------------------------------
  151.  
  152. This is the routine that accomplishes step 9) above.  It does this by
  153. moving to each intersection point in the cmaze display array interior
  154. for the horizontal and vertical walls (see fillmaze.c, below) of the
  155. rooms, and looking at the north, south, east and west display
  156. characters; if they are all blanks, then this is an isolated "pillar"
  157. piece of wall, so it is replaced by a blank.
  158.  
  159.  
  160. -----------------------------------------------------------------------
  161. | closedoors.c -- closedoors()
  162. -----------------------------------------------------------------------
  163.  
  164. This is the routine that accomplishes step 7) above.  It does this by
  165. walking the DEAD list from list head pointer dead until a NOPOINTER
  166. next pointer is found.  At each room on the dead list, it counts the
  167. doors by looking north, east, south, and west, then if more than one
  168. exists, picks one at random to survive and replaces the others with
  169. WALL symbols.  Most of the nasty looking arithmetic is just the
  170. translation from statlist indices to cmaze locations; all the code is
  171. rife with them.
  172.  
  173. The necessity for this routine is that when the previous street
  174. creation routines are complete, most of the room cells have most walls
  175. as doors, which doesn't make for much of a challenge, the adventurer
  176. doesn't have to work down the street, but can instead just walk
  177. through the row of rooms to the street behind. 
  178.  
  179. Also, games like Bard's Tale are simpler to design if all normal rooms
  180. have only one exit, since then only one viewpoint and no choices need
  181. be presented to the player. 
  182.  
  183. -----------------------------------------------------------------------
  184. | closegates.c -- closegates()
  185. -----------------------------------------------------------------------
  186.  
  187. This is the routine that accomplishes step 8) above.  It does this in
  188. response to a comparision between the -g mazegates paramenter and the
  189. -l leftgates parameter; if more gates were seeded than were requested
  190. in the final maze, then gates are closed at random until only
  191. leftgates of them remain.
  192.  
  193. How many places along the maze periphery must be checked for gates is
  194. precomputed into possiblegates, which becomes the inner (for) loop
  195. limit.
  196.  
  197. The gate closing task is done by keeping track of how many gates are
  198. left with gatesleft, conditioning an outer while loop on gatesleft
  199. being greater than leftgates, then withing the loop rolling a random
  200. gate number into chosengate as a modulus of RANDOM() by gatesleft, and
  201. then walking the periphery of the maze in and inner for loop, looking
  202. for VDOOR or HDOOR elements in the outer wall, and counting how many
  203. gates have been encountered in foundgate.
  204.  
  205. When the gate encountered matches the number found, the gate is
  206. closed, the gatesleft decremented, and the inner loop broken.
  207.  
  208. The messy interior of the inner loop is unavoidable unless a link list
  209. of gate cells is kept, which would have messed up statlist too much;
  210. the arithmetic to walk around the border of a rectangle when the
  211. indices are in row major order instead of some nice inturning spiral
  212. is just that ugly.
  213.  
  214.  
  215. -----------------------------------------------------------------------
  216. | connectst.c -- connectstreets()
  217. -----------------------------------------------------------------------
  218.  
  219. This is the routine that accomplishes step 5), above.  The goal is to
  220. make all the streets in the maze into one connected street, decreasing
  221. streetnumct to 1, and this is where townmaze spends most of its time,
  222. since adding new street cells is what makes the maze.
  223.  
  224. This is one of the biggest routines in townmaze, because it needs
  225. three separate strategies to connect streets.  First it tries to
  226. connect all the streets by only using cells from the LIVE list in
  227. statlist.  If all the LIVE cells are used up and there are still more
  228. than one street noted in streetnumct, then the second strategy is to
  229. use only DEAD cells that touch two different streets as new street
  230. cells.  If worst comes to worst, if streetnumct is still greater than
  231. one, then in the third strategy, the entire DEAD cell list is used as
  232. targets to connect the streets.
  233.  
  234. This routine runs a lot slower than first glance might suggest,
  235. because called routine makestreet() can succeed as infrequently as
  236. once in a thousand calls, depending on the current configuration of
  237. the maze, and the mazestraightness parameter setting.
  238.  
  239. The first phase is done by picking a random LIVE list element, walking
  240. the live list to that element, and asking makestreet() to make a
  241. street of it.  The outer loop doing this is conditioned on both
  242. streetnumct being greater than 1, and livect being greater than zero.
  243. The fact that makestreet() might refuse to make a street on
  244. straightness criteria is hidden by just running the outer loop until
  245. one of the conditions fails. 
  246.  
  247. Afterthe outerloop selects a targetcell from the LIVE list based on a
  248. modulus of RANDOM() against the livect, the middle loop is used to
  249. walk livewalk down the LIVE list. This is where the maintenance of the
  250. links lists pays off; without them, the walk to find the targetcell
  251. among the LIVE list cells would have to walk the whole statlist, a
  252. much longer list. 
  253.  
  254. In the inner loop, nhbrexists is used, here as elsewhere, to avoid
  255. indexing an invalid entry of statlist, while nhbris is used to
  256. translate simply from a neighbor number 0-3 to a statlist cell index.
  257. If you want to be nice about it you could call this data abstraction.
  258.  
  259. The call to makestreet() contains "-1" as the last parameter, which
  260. enables the straightness checks there.
  261.  
  262. The second phase is done by walking the DEAD list, seeking DEAD cells
  263. whose neighbors include streets with two or more different numbers.
  264. This could probably have been done more cleanly with the "for(i" loop
  265. replaced by a "while (deadwalk != NOPOINTER)" loop, but this one
  266. works.  Because the DEAD list is changing size while the list is
  267. walked, there are some hyper-defensive checks going on to make sure
  268. the DEAD list end isn't exceeded.  For the same reason, deadwalk has
  269. to be moved past the current cell before it is possibly yanked out
  270. from under by makestreet (which will move it from the DEAD list to
  271. the STREET list if called), so lastdead is used to access the current
  272. cell and then not used after the makestreet call.
  273.  
  274. At the top of the loops, a check is made that this cell is interior;
  275. the goal is to avoid running streets along the city wall, a boring
  276. kind of design; take this check out if you want the occasional wall
  277. hugger.  The double loop on j and k is comparing neighbors, where they
  278. exist, looking for streets with different numbers. 
  279.  
  280. To avoid trying to break out of a double loop, not one of C's strong
  281. points, another trick was used.  The reason this loop doesn't keep
  282. trying to make a street out of the chosen cell after it has done so
  283. once is that makestreet() causes all the adjacent streets to be merged
  284. and have the same street number, so that the inner if test always
  285. fails after the first success. 
  286.  
  287. he call to makestreet is with a forcing actual direction last
  288. parameter (see below about cheapdir) so the street is always created
  289. on the first try. Except, that if the street is a neighbor of an
  290. UNUSED cell, then the check at the very top of makestreet() means
  291. intsead that it will never be made into a street. 
  292.  
  293. The double j, k loop can thus in either case safely be let run to
  294. completion, since it will never ask makestreet() to make a street into
  295. a street.  Letting it spin through to completion is no big deal since
  296. phase two is a very minor part of the overall time in any case, doing
  297. no random calls. 
  298.  
  299. The little trick down inside the loop with cheapdir is to adopt the
  300. direction of one of the streets whether this cell continues it or not,
  301. and the second one if it happens to continue the street.  This is not
  302. perfectly fair, but it doesn't seem to hurt anything, and with at
  303. least two sides already streets, this cell is less likely than many to
  304. want to continue its direction into a subsequently-started-here alley
  305. or street.
  306.  
  307. The third phase is much like the first, except that now the DEAD list
  308. is being randomly used to extend the streets, rather than the live
  309. list.  This tends to make the town maze more open, and to destroy the
  310. back to back rooms that are the whole goal of townmaze, but it is
  311. sometimes the only way to "make ends meet" and get all the streets
  312. connected, an absolute requirement. for townmaze.  The only difference
  313. from the first phase is that, unlike LIVE cells, which are always
  314. interior (to keep the streets from running down the city wall again),
  315. DEAD cells may be border cells, and we want explicitly to exclude the
  316. border cells from consideration for extending the streets.
  317.  
  318.  
  319. -----------------------------------------------------------------------
  320. | filllist.c -- filllist()
  321. -----------------------------------------------------------------------
  322.  
  323. This routine does half of step 3), above.  This function takes the raw
  324. space allocated for statlist by makespace(), and turns it into an
  325. array which is also five doubly linked lists, all but the ISOLATED
  326. list empty and with list heads set to NOPOINTER, and list counts set
  327. to zero, to start.  The ISOLATED list count is set to listsize, and
  328. the list header pointing to the zeroth element of statlist.  Just at
  329. the end it puts the four corner cells into the UNUSED list using
  330. movefromto(), and sets the streetnumct to zero (it needed to be done
  331. somewhere in setup, or static set there, and I prefer the self
  332. documentation of an explicit action). 
  333.  
  334.  
  335. -----------------------------------------------------------------------
  336. | fillmaze.c -- fillmaze()
  337. -----------------------------------------------------------------------
  338.  
  339.  
  340. This routine does the other half of step 3), above.  This function
  341. makes every even (zero origin, remember) row and column of the cmaze
  342. character array a WALL symbol, and the remaining doubly odd coordinate
  343. interstices BLANK symbols, and at the end fills in WALL symbols in the
  344. four corner cells' centers to make them UNUSED looking. 
  345.  
  346.  
  347. -----------------------------------------------------------------------
  348. | finishalleys.c -- finishalleys()
  349. -----------------------------------------------------------------------
  350.  
  351. This is the routine that accomplishes step 6), above.  After
  352. connectstreets() has made sure that all the streets in town are
  353. interconnected, there may still be ISOLATED (and thus LIVE) cells
  354. remaining.  This routine continues to turn LIVE cells into streets,
  355. and thus ISOLATED cells into LIVE or DEAD cells and possible
  356. consequently LIVE cells into DEAD cells (from lack of a neighboring
  357. ISOLATED cell and more) until no LIVE or ISOLATED cells remain. 
  358.  
  359. This works just like the first phase of connectstreets(), except that
  360. it is no longer conditioned on streetnumct > 1, just on livect > 0. 
  361.  
  362. [By the way, there's no special significance to "alleys" as opposed to
  363. "streets", the name just seemed homeyer.]
  364.  
  365.  
  366. -----------------------------------------------------------------------
  367. | freespace.c -- freespace()
  368. -----------------------------------------------------------------------
  369.  
  370. This routine does step 11), above.  Sigh.  There really ought to be a
  371. library routine to allocate, and another to free, the ugly messes C
  372. uses for multidimensional arrays.  So far as I can figure, you can
  373. allocate a fixed size array without the intermediate pointer list only
  374. at compile time, since there is no way to convince the compiled code
  375. at run time that it knows the major dimension, so regular indexing
  376. won't work for an array dynamically allocated into an array declared
  377. "cmaze[][]", and the compiler complains if you try. 
  378.  
  379. In any case, this does the standard stuff to give back the storage for
  380. statlist and cmaze.  On a Unix box this is excessive neatness, but on
  381. an Amiga that doesn't do resource tracking, this is mandatory to avoid
  382. "leaking" memory and forcing an eventual reboot. 
  383.  
  384. Error checking is done on the free() calls; I don't have any recourse
  385. if an error occurs in any case, but at least I can complain before
  386. dying. 
  387.  
  388.  
  389. -----------------------------------------------------------------------
  390. | getargs.c -- getargs(argc,argv)
  391. -----------------------------------------------------------------------
  392.  
  393. This routine accomplishes step 1) above.  Sigh again.  Lattice C for
  394. the Amiga doesn't have getopts() as a library routine, so I wrote this
  395. special purpose beast.  To avoid intensely ugly code, the nice
  396. getopts() feature of accepting both spaces and no spaces between the
  397. flag and the value is foregone here; I just don't have that much
  398. patience.  Leaving the space only option makes this code neat and
  399. easy. 
  400.  
  401. The routine starts by checking for any arguments, and if none just
  402. goes down to compute listsize and (compile time ooptionally) do a
  403. header and return.  If there are an odd number of argv entries (the
  404. function name is counted in argc, so this means that flags and values
  405. come in pairs), the parameters are fetched in order into the
  406. appropriate global parameter variables.  If an even number of argv
  407. entries exist, the routine complains and exits.
  408.  
  409. The switch statement is pretty standard; the only interesting element
  410. is the -r switch, which loads a long instead of an integer, and also
  411. calls SEEDRANDOM(randomseed) to override the previous (in main()) call
  412. to SEEDRANDOM(time()).  This lets the random seed be forced, allowing
  413. duplicate runs for debugging or for inclusion in a game where the code
  414. and seeds are cheaper to store than the rooms. (Big game!)
  415.  
  416. The check below the switch is about half of the sanity in the whole
  417. program:
  418.  
  419. (mazeheight < 11) -- The maze must be at least five cells wide to
  420. leave room for one interior row of buildings. 
  421.  
  422. ((mazeheight%2) != 1) -- The maze height must be odd.
  423.  
  424. (mazewidth < 11) -- The maze must be at least five cells high to leave
  425. room for one interior row of buildings. 
  426.  
  427. ((mazewidth%2) != 1) -- The maze width must be odd.
  428.  
  429. The above four checks are order sensitive in the error messages they
  430. produce, because C (at least the ones I have here) implements modulus
  431. of a negative number wrong, so that ((-1)%2 != 1) comes out true.
  432.  
  433. (mazegates < 0) -- There must be at least zero gates.
  434.  
  435. (mazegates > (2*((mazeheight - 6)/7) + 2*((mazewidth- 6)/7))) -- There
  436. can be no more gates than one per three side cells between the unused
  437. corner cells, to assure that all gates will fit. 
  438.  
  439. (leftgates < 0) -- There must be at least zero gates left at the end.
  440.  
  441. (leftgates > mazegates) -- There cannot be more gates left than there
  442. were to begin. 
  443.  
  444. (mazecourts < 0) -- There must be at least zero courtyards.
  445.  
  446. (mazecourts > (((mazeheight - 5)/6)*((mazewidth - 5)/6))) --
  447. Courtyards must have room for at least two spaces between them,
  448. otherwise they won't fit when makecourts() is run.
  449.  
  450. ((mazecourts + mazegates) < 1) -- There must be at least one street.
  451.  
  452. (mazeunused < 0) -- There must be at least zero unused cells.
  453.  
  454. (mazeunused > (((mazeheight - 1)/14)*((mazewidth - 1)/14))) -- Unused
  455. cells must have room for at least three squares between them,
  456. otherwise they can trap DEAD cells away from streets and won't fit when
  457. makeunused() is run.. 
  458.  
  459. (mazestraightness < 0) -- Negative straightness makes no sense.  [Zero
  460. means don't do straightening, and that's OK.]
  461.  
  462. (mazestraightness > 998) -- There must be at least one chance per
  463. thousand of accepting a bend in the street, or an infinite loop might
  464. occur. 999 is the highest value returned by the random roll modded
  465. against 1000, so 998 is the largest accpetable straightness parameter. 
  466.  
  467. The above checks now bump an error counter, and if the sanity check
  468. drops out for any reason, an appropriate message telling which parameter
  469. was wrong, and why, is printed. Since the values read in OK, but were
  470. just inappropriate, the header line optional below to stdout is forced
  471. here to stderr to show the user what the program thinks s/he said.
  472.  
  473. Similar error reporting is now in the switch statement cases for
  474. unreadable parameters.
  475.  
  476. If all the parameters are right, the listsize (number of cell
  477. structures in statlist) is computed from the requested maze width and
  478. height. If the HEADER option is on (I always use it) a single line of
  479. parameters is printed to standard out along with the eventual maze.
  480. [All other output goes to the standard error unit.]
  481.  
  482.  
  483.  
  484.  
  485. -----------------------------------------------------------------------
  486. | interiorcell.c -- interiorcell(cellid)
  487. -----------------------------------------------------------------------
  488.  
  489. This simple function just checks that the passed cell has all four
  490. neighbors using nhbrexists().  Lots of stuff in townmaze specifies
  491. interior cells only. 
  492.  
  493.  
  494. -----------------------------------------------------------------------
  495. | makecourts.c -- makecourts()
  496. -----------------------------------------------------------------------
  497.  
  498. This routine does step 4c), above.  In response to the mazecourts
  499. parameter value, this routine places "courtyards", STREET cells in the
  500. interior of the town maze, spaces them out by making sure before they
  501. are placed that all neighbor cells and the randomly chosen cell are
  502. ISOLATED cells, and uses a side effect of makestreet() to turn them
  503. into LIVE or DEAD cells as each courtyard is placed, effectively
  504. fending off neighbors a bit. 
  505.  
  506. Because there are so many ISOLATED cells when this is done, it is
  507. better to roll the randomizer agains the whole maze and accept the
  508. misses where a non-ISOLATED cell is chosen, than to roll against the
  509. length of the ISOLATED list and walk it from the end each time.  If
  510. one habitually made the courtyard cells very high, it might be better
  511. to do this the other way. 
  512.  
  513. The random roll could be improved, but for small mazes it doesn't
  514. matter much.  The bias of ignoring the mismatch between the modulus
  515. and the length of the interval returned by RANDOM() in a maze 32K on a
  516. side would probably be pretty obvious. 
  517.  
  518. Because it was easier than explicitly compensating for the
  519. interference from UNUSED cells, a MAXTRIES limit is enforced for
  520. placing each courtyard cell.  The limit is kept very high to prevent
  521. interference with the straightness parameter other places that it is
  522. used, but with it in here, makecourts is guaranteed to terminate
  523. eventually.  On the other hand, it is not guaranteed to place as many
  524. courtyards as the user specified, which is one reason for the sanity
  525. check at the start of connectstreets(). 
  526.  
  527.  
  528. -----------------------------------------------------------------------
  529. | makegates.c
  530. -----------------------------------------------------------------------
  531.  
  532. This routine does step 4b), above.  In response to the makegates
  533. parameter value, this routine places "gates", STREET cells along the
  534. border of the town maze, spacing them apart by insisting that all the
  535. existing neighbors when each gate cell is placed be ISOLATED cells,
  536. including the chosen cell itself.  It uses makestreet() as each gate
  537. cell is placed, to turn the chosen cell into a street, its border cell
  538. neighbors into DEAD cells, and its interior neighbor into a LIVE or
  539. DEAD cell depending on that neighbor's neighbors. 
  540.  
  541. It would have been possible to just roll a randomizer against the
  542. whole statlist array, and then check the border and isolated
  543. properties, but for a very large town maze, this would have been
  544. grossly inefficient and slow, so the more complex method seen is used,
  545. instead.  The random roll is effectively done against the number of
  546. cell wallss in the town maze border, the border is walked using the
  547. logic that comprises the whole middle of this routine, and if the
  548. chosen wall meets the criteria of ISOLATED self and neighbors, it is
  549. selected.  This can still be slow if gates are dense, but not nearly
  550. as bad as scattershooting at the whole area. 
  551.  
  552. As explained above for closegates(), the ugly arithmetic in the middle
  553. border walking logic is fairly inevitable, though perhaps it should be
  554. hidden like nhbrexists() hides similar arithmetic.  Maybe next time.
  555.  
  556. Notice that, because we can't abstract away with interiorcell(), we
  557. must explicitly check nhbrexists() for each cell before using
  558. nhbris() to index statlist.
  559.  
  560. Also, the test against MAXTRIES may not be needed here, but it was
  561. easier to put in the check than to do the analysis to prove it wasn't
  562. needed, and it makes future code changes like allowing non-corner
  563. UNUSED border cells more robust.
  564.  
  565.  
  566. -----------------------------------------------------------------------
  567. | makestreet.c -- makestreet(chosencell,streetid,streetpoints)
  568. -----------------------------------------------------------------------
  569.  
  570. This routine is charged with doing all the work to make a single cell
  571. into a street cell, including updating all the neighbor cells whose
  572. status changes because this cell became a street cell, and doing all
  573. the corresponding architectural changes.  It is also tasked with
  574. enforcing the straightness parameter, which means that it gets to say
  575. "no I won't" a lot when told to make a street, and all the routines
  576. that call it have to either override that option with a explicit
  577. direction in the "streetpoints" parameter, or survive not being
  578. obeyed.  Not surprising that makestreet is a big routine.
  579.  
  580. It starts out by refusing to build a street alongside an UNUSED cell,
  581. since the point of an UNUSED cell is to have all neighbors be rooms.
  582. It just returns immediately.
  583.  
  584. Next, it checks the last parameter, and if it is "-1" (no street
  585. direction selected) enables the straightness check and does it by
  586. making sure that the selected cell can continue some street's current
  587. direction.  If not, it returns immediately.
  588.  
  589. Next, it moves the chosen cell to the STREET list using movefromto().
  590.  
  591. Next, it copies the streetid parameter to the cells
  592. statlist[].streetnum field. 
  593.  
  594. Next, it updates the cmaze display version of the town maze.  As a
  595. first guess, it makes all four walls into doors. 
  596.  
  597. Next, it starts working on the first order neighbors.  If the neighbor
  598. is a STREET, then the door becomes a BLANK or passageway.  If the
  599. chosen cell would continue the street direction, adopt that neighbor's
  600. street direction for this cell; keep the last one thus adopted.  If it
  601. wouldn't continue an existing street direction, defer this matter for
  602. the bottom of the routine.
  603.  
  604. Next, a very important check is done to see if a detected neighbor
  605. street has a different street number than this streets number (passed
  606. in as streetdir).  If so, the higher number street is updated by a
  607. walk of the streetlist with the street number of the lower numbered
  608. street, and the streetnumct is decremented.  The two driving goals of
  609. the whole townmaze routine are to make streetnumct 1 and isolatedct 0.
  610.  
  611. If the neighbor is LIVE, make it DEAD and then check its neighbors and
  612. if one is still ISOLATED, make it LIVE again.
  613.  
  614. If the neighbor is ISOLATED, things get complicated.  This was hard
  615. enough to see that it was the last big logic bug in the program.  If
  616. the neighbor is ISOLATED, it becomes LIVE or DEAD, as appropriate,
  617. except that border cells always become dead to keep the roads off the
  618. walls as mentioned previously, and neighbors of UNUSED cells never
  619. become LIVE (because they aren't eligible to become streets), but
  620. always go from ISOLATED to DEAD. 
  621.  
  622. This change, however, might mean that the formerly ISOLATED cells LIVE
  623. neighbors, if any, no longer qualify as LIVE.  So, each of those
  624. secondary neighbors is checked, and if it is LIVE, made DEAD, and then
  625. each of its (now tertiary) neighbors is checked in turn, and if it is
  626. ISOLATED, the DEAD cell is revived to LIVE again.  Whew! Making this
  627. work was what achieved back to back rows of rooms in the town maze
  628. after long programming effort.
  629.  
  630. Next, the question of street direction for the new STREET cell is
  631. revived.  If some of the above overroad the "-1" that was installed by
  632. filllist(), that value is retained.  If not, and a valid (not -1)
  633. direction was passed in streetpoints, that value is adopted.  If not,
  634. the direction from a neighboring street is adopted, and this becomes a
  635. turning point in the street.  Note that the effect of the whole
  636. routine is that if the straightness check near the top found a
  637. direction that this street could continue an existing street, then it
  638. does continue some street, so we only get down here and turn the
  639. street if the turn direction was passed in, or if the dice roll was
  640. such as to allow a bend.
  641.  
  642. At the end, makestreet returns a True value; it several other places
  643. returns False; I'm not sure this is currently used anywhere, but if
  644. desired for your code, the return value tells whether a street cell
  645. was in fact created.
  646.  
  647.  
  648. -----------------------------------------------------------------------
  649. | makespace.c -- makespace()
  650. -----------------------------------------------------------------------
  651.  
  652. This routine does step 2) above.  The predecessor to freespace(), this
  653. routine uses malloc() to allocate space for the statlist and cmaze
  654. arrays, using the usual grotesque C mechanisms.  It is very defensive
  655. of freeing every bit of acquired memory if a malloc() call fails,
  656. because on the Amiga, malloc()ed memory is lost if not free()d before
  657. exit. 
  658.  
  659.  
  660. -----------------------------------------------------------------------
  661. | makeunused.c -- makeunused()
  662. -----------------------------------------------------------------------
  663.  
  664. This routine does step 4a), above.  In response to parameter
  665. mazeunused, this routine makes some of the interior cells of the town
  666. maze have UNUSED status and a WALL symbol in the center.  It looks
  667. grotesque, but that's just because it checks the chosen cell and its
  668. 48 closest neighbors to be ISOLATED before accepting selection of the
  669. cell for the UNUSED list. 
  670.  
  671. Attempts are controlled by MAXTRIES, and, since this is the hardest
  672. seed item to place, it should be done first.  The reason for the wide
  673. band of ISOLATED cells is to assure that UNUSED cells, which cannot
  674. have streets beside them, have at least three rows of cells between
  675. them, so street connection is not blocked. 
  676.  
  677. At the top of the routine, a choice existed to use simple math and
  678. inefficient surplus random calls, or complex math so that only
  679. interior cells were eligible for the random call.  The simple,
  680. wasteful method was chosen, since the mazeunused parameter is
  681. carefully limited to make sure the UNUSED cells will actually fit, and
  682. the number used is zero by default.  So totalcells is just the list
  683. size, and is used for the modulus against the RANDOM() call. 
  684.  
  685. The outer loop is pretty simple, it just counts the requested
  686. mazeunused cells whose creation is attempted, attempts to find one to
  687. create upto to MAXTRIES attempts, and if one is found, as opposed to
  688. falling through on MAXTRIES, moves it to the UNUSED list with
  689. movefromto(), and makes the center a WALL symbol in cmaze with the
  690. usual ugly arithmetic to convert from statlist to cmaze indices. 
  691.  
  692. The inner loop is simpler yet, with just two statements, the random
  693. cell choice to try, and a bump of tries to check each round against
  694. MAXTRIES. 
  695.  
  696. What was a set of guard conditions for the do{}while is now a separate
  697. routine, to allow smaller compilers to compile the code.
  698.  
  699. The new routine wimpy_cc() does 59 guard conditions for the do{}while,
  700. perhaps some minor record.  Dijkstra would be proud. There is really a
  701. lot less than meets the eye.  First, the tries against MAXTRIES test
  702. is done in the do{}while condition.
  703.  
  704. Next, wimpy_cc() is called.  There, enough interiorcell() checks are
  705. done to assure that all 49 cells exist, each check depending on some
  706. one before for the existance of the next cell to check.  Last, all 49
  707. cells are checked to be ISOLATED cells. The thing that makes it look
  708. messy is that the nbhris() method of finding a neighbor is used rather
  709. than defining several extra functions to accomplish the same thing
  710. with fewer lines of code.
  711.  
  712. Again, mainly defended by this being a low cpu cost part of the code. 
  713.  
  714. -----------------------------------------------------------------------
  715. | movefromto.c -- movefromto(&fromlist,&fromcount,&tolist,&tocount,
  716. |                            newstat,cellnum)
  717. -----------------------------------------------------------------------
  718.  
  719. This lovely little routine is used to hide almost all of the details
  720. of fairly tricky double linked list maintenance from the rest of the
  721. code. 
  722.  
  723. Nothing terribly original is going on here; the cell pointers are
  724. saved, the cell is unlinked from the (middle of the) from list and
  725. linked into the head of the to list, the saved pointers are used to
  726. repair the rest of the from list, and the cell status is updated and
  727. the from and to counts corrected.. 
  728.  
  729. There is a little complexity because the list heads are "pointers"
  730. (indices really) rather than whole list elements, but that is one of
  731. the two standard approaches. 
  732.  
  733. -----------------------------------------------------------------------
  734. | nhbrexists.c -- nhbrexists(cellid,nhbrid)
  735. -----------------------------------------------------------------------
  736.  
  737. This simple little routine just converts the statlist cell index to
  738. cell row and cell column, something that corresponds to no existing
  739. array in the program, and then checks to see if the neighboring cell
  740. in the nhbrid direction (0==north==up; 1==east==right; 2==south==down;
  741. 3==west==left) falls inside the bounds of the cell row and column
  742. limits and returns true or false. 
  743.  
  744. -----------------------------------------------------------------------
  745. | nhbris.c -- nhbris(cellid,nhbrid)
  746. -----------------------------------------------------------------------
  747.  
  748. This equally simple routine depends on the neighbor cell being known
  749. to exist, and returns its statlist cell index given the current cell
  750. index and the neighbor direction.
  751.  
  752. -----------------------------------------------------------------------
  753. | showdbmaze.c -- showdebugmaze()
  754. -----------------------------------------------------------------------
  755.  
  756. This routine is for debug, and is linked in but only called in error
  757. conditions unless the SHOWSTART define is compiled set to 1.
  758.  
  759. It does give a very handy debugging display, though, with the cell
  760. centers of non-STREET cells replaced by letters showing the cell
  761. status, I, L, D, U, or ? for bad status, and the centers of STREET
  762. cells replaced by the direction of that street cell as ^ > v < or X
  763. for bad direction. 
  764.  
  765. This lets one dump (in one case where I used it) a running list of
  766. mazes, with a single street cell more progress for each, and visualize
  767. how the algorithm is working. I used this display to find the problem
  768. with tertiary neighbors of ISOLATED cells mentioned above under
  769. makestreet(). 
  770.  
  771. The method is to walk the entire cmaze character array, dumping the
  772. array contents for all but center-of-cell cmazearray characters (the
  773. ones with both indices odd), and, using a big switch statement on cell
  774. status and a nested smaller switch statement on street direction for
  775. status STREET, to chose the status or direction symbol to print in
  776. place of the usual BLANK or WALL center symbol. 
  777.  
  778. The default: entries for the switch statements are bogosities, though
  779. cute, but it's hard to put obsessively detailed debug statements into
  780. what are already debug routines, so I didn't. 
  781.  
  782. -----------------------------------------------------------------------
  783. | showlistdet.c -- showlistdetail()
  784. -----------------------------------------------------------------------
  785.  
  786. Another debug routine, calls to which are commented out in main().
  787. This and showlistsummary() together dump a detailed printout of the
  788. contents of statlist.  Since mountains of data are useless for
  789. debugging, no attempt has been made to format this nicely for big
  790. mazes; do your debugging with little ones. This part dumps the
  791. contents of each cell of the statlist, the prev, next, status,
  792. streetnum and streetdir fields. 
  793.  
  794. -----------------------------------------------------------------------
  795. | showlistsum.c -- showlistsummary()
  796. -----------------------------------------------------------------------
  797.  
  798. Another debug routine, calls to which are commented out in main().
  799. This and showlistdetail() together dump a detailed printout of the
  800. contents of statlist and the list counts and pointers.  This routine
  801. dumps the list identifier integer value, the list header pointer
  802. contents, and the list count for each of the isolated, live, dead,
  803. street, and unused lists, and with the street list items, the
  804. streetnumct global contents as well. 
  805.  
  806. -----------------------------------------------------------------------
  807. | showmaze.c -- showmaze()
  808. -----------------------------------------------------------------------
  809.  
  810. The routine that does step 10), show the final product, is extremely
  811. simple, just a row by row dump of the cmaze array with trailing
  812. newlines.  The little #if condition on PROGRESS is so that the first
  813. line of the maze won't get dumped starting in midline when the
  814. optional progress reports are turned on and showmaze() is being used
  815. along with showdbmaze() for debugging in mid run. 
  816.  
  817. -----------------------------------------------------------------------
  818. | townmain.c -- main(argc,argv)
  819. -----------------------------------------------------------------------
  820.  
  821. This is the main routine for townmaze.  It just calls routines to do
  822. the steps listed at the top in order: seeds the random number
  823. generator, gets the arguments from the command line, allocates array
  824. space, fills the cmaze array, fills the statlist array, seeds the
  825. unused cells, seeds the gate cells, seeds the courtyard cells,
  826. connects the streets, runs alleys to the remaining isolated cells,
  827. closes off the extra doors open for each room, closes off any extra
  828. open gates, shows the maze, returns the allocated array space to the
  829. free list, and exits (step 12), uniquely a part of main()).
  830.  
  831. -----------------------------------------------------------------------
  832. | usage.c -- usage()
  833. -----------------------------------------------------------------------
  834.  
  835. This routine is called by getargs if any of the command line arguments
  836. are incorrect or unparsable, it just dumps a 22 or so line usage
  837. message on the screen describing the parameters and their purpose
  838. briefly. 
  839.  
  840.